package framework.common;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

/**
 * 複数の子センサを接続する親センサ<br/>
 * デバイスと直接接続されるルートセンサに名前はない。<br/>
 * 子の名前は親が決める。<br/>
 * 例えば、名前aの子センサの端子xはa.xとなる。区切り文字は"."である。<br/>
 * 孫センサは直接アクセスできないので、子センサからアクセスする。
 *
 * @author uehara
 *
 */
public class MultiSensor extends Sensor {

	private Map<String, Sensor> sensors;

	public MultiSensor() {
		sensors = new HashMap<String, Sensor>();
	}

	/**
	 * 子センサを接続する。
	 *
	 * @param name
	 *            子センサの名前
	 * @param child
	 *            子センサ
	 */
	public void attach(String name, Sensor child) {
		sensors.put(name, child);
		child.setParent(this);
	}

	/**
	 * 子センサを取り外す。
	 *
	 * @param name
	 *            子センサの名前
	 */
	public void detach(String name) {
		sensors.remove(name);
	}

	/**
	 * すべての子センサを取り外す。
	 */
	public void clear() {
		sensors.clear();
	}

	/**
	 * すべての子センサの名前を返す。
	 *
	 * @return 子センサの名前の集合
	 */
	public Set<String> getNames() {
		return sensors.keySet();
	}

	/**
	 * 指定した子センサが接続されているかどうかを調べる。
	 *
	 * @param name
	 *            子センサの名前
	 * @return 接続されていれば真、そうでなければ偽
	 */
	public boolean hasChild(String name) {
		return sensors.containsKey(name);
	}

	/**
	 * 指定した子センサを返す。
	 *
	 * @param name
	 *            子センサの名前
	 * @return 子センサ
	 */
	public Sensor getChild(String name) {
		return sensors.get(name);
	}

	@Override
	public SensorResponse sense(SensorRequest request) {
		String attr = request.getAttribute();
		for (String name : getNames()) {
			if (attr.startsWith(name + SEPARATOR)) {
				String a = removePrefix(attr, name + SEPARATOR);
				request.setAttribute(a);
				Sensor child = getChild(name);
				SensorResponse res = child.sense(request);
				res.setAttribute(attr);
				return res;
			}
		}
		throw new RuntimeException("no such attribute");
	}

	@Deprecated
	List<String> split(String path, String separator) {
		List<String> names = new LinkedList<String>();
		Scanner s = new Scanner(path).useDelimiter(separator);
		while (s.hasNext()) {
			names.add(s.next());
		}
		return names;
	}

	@Override
	public Set<String> getAttributes() {
		Set<String> attributes = new HashSet<String>();
		for (String name : getNames()) {
			for (String attribute : getChild(name).getAttributes()) {
				attributes.add(name + SEPARATOR + attribute);
			}
		}
		return attributes;
	}

	public static final String SEPARATOR = ".";

	String removePrefix(String s, String prefix) {
		if (s.startsWith(prefix)) {
			return s.substring(s.indexOf(SEPARATOR) + 1);
		}
		return s;
	}

	@Override
	public Object getAttribute(String attr) {
		for (String name : getNames()) {
			if (attr.startsWith(name + SEPARATOR)) {
				String a = removePrefix(attr, name + SEPARATOR);
				return getChild(name).getAttribute(a);
			}
		}
		return null;
	}

	@Override
	public void setAttribute(String attr, Object value) {
		for (String name : getNames()) {
			if (attr.startsWith(name + SEPARATOR)) {
				String a = removePrefix(attr, name + SEPARATOR);
				getChild(name).setAttribute(a, value);
				return;
			}
		}
	}

}
